import java.util.HashMap;

public class Minimax {
	private final static int cutoffdepth = 12;
	public int playerID, opponentID;
	private boolean returnTwice = false;
	private HashMap<State, Integer> transTbl = new HashMap<State, Integer>(100000);
	private static int hitCounter = 0, count = 0;
	private ISeq seq = new Seq();
	private GameLogic gameLogic;
	private enum CutoffActions {finalState, cutoff, playOn}
	
	public Minimax(int playerID, int opponentID, GameLogic gameLogic){
		this.playerID = playerID;
		this.opponentID = opponentID;
		this.gameLogic = gameLogic;
	}
	
	public int Decision(State s){
		int pick = Integer.MIN_VALUE; 
		int v = Integer.MIN_VALUE;
		for (Integer column : s.openCols){
			State sPrime = new State(s);
			sPrime.insertCoin(column, playerID);
			int val = MinValue(sPrime, Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
			if (val > v){
				pick = column;
				if (val > 0) break;
				v = val;
			}
		}
		System.out.println("hits: " + hitCounter);
		System.out.println("count: " + count);
		System.out.println("tvlSize: " + transTbl.size() + "\n");
		count = hitCounter = 0;
		return pick;
	}
	
	public int MaxValue(State s, int alpha, int beta, int depth){
		count++;

		CutoffActions action = cutoffTest(s, depth);
		if (action != CutoffActions.playOn){
			if (action == CutoffActions.finalState){
				int utility = gameLogic.TerminalTest(s);
				if (utility == playerID) return Integer.MAX_VALUE-1;
				else if (utility == opponentID) return Integer.MIN_VALUE + depth;
				else return -1000 + depth;
			}
			else if (action == CutoffActions.cutoff) return eval(s);
		}
		
		int v = Integer.MIN_VALUE;
		for (Integer column : s.openCols){
			State sPrime = new State(s);
			sPrime.insertCoin(column, playerID);
			int val;
			if (transTbl.containsKey(sPrime)) {
				val = transTbl.get(sPrime);
				hitCounter++;
			} else {
				val = MinValue(sPrime, alpha, beta, depth+1);
				if (val > 0 && depth < 16 ) {
					transTbl.put(sPrime, val);
				}
			}
			if (val > v){
				if (val > 0) return val;
				v = val;
			}
			if (v >= beta) return v;
			if (v > alpha) alpha = v;
		}
		return v;
	}
	
	public int MinValue(State s, int alpha, int beta, int depth){
		count++;
		CutoffActions action = cutoffTest(s, depth);
		if (action != CutoffActions.playOn){
			if (action == CutoffActions.finalState){
				int utility = gameLogic.TerminalTest(s);
				if (utility == playerID) return Integer.MAX_VALUE-1;
				else if (utility == opponentID) return Integer.MIN_VALUE + depth;
				else return -1000 + depth ;
			}
			else if (action == CutoffActions.cutoff) return eval(s);
		}

		int v = Integer.MAX_VALUE;
		for (Integer column : s.openCols){
			State sPrime = new State(s);
			sPrime.insertCoin(column, opponentID);
			int val = MaxValue(sPrime, alpha, beta, depth+1);
			if (returnTwice){
				returnTwice = false;
				return val;
			}
			if (val < v){
				v = val;
			}
			if (v <= alpha) return v;
			if (v < beta) beta = v;
		}
		return v;
	}
	
	private CutoffActions cutoffTest(State state, int depth){
		if (gameLogic.TerminalTest(state) != 0){
			return CutoffActions.finalState;
		}
		if (depth >= cutoffdepth){
			return CutoffActions.cutoff;
		}
		return CutoffActions.playOn;
	}
	
	private int eval(State state){
		// Implement state traversal here, Lars!
		return seq.value();
	}
}
